package com.hzgj.bcl.soa.command;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hzgj.bcl.soa.cicada.router.CicadaRouter;
import com.hzgj.bcl.soa.collector.Collectors;
import com.hzgj.bcl.soa.collector.JmxCollector;
import com.hzgj.bcl.soa.config.ServiceDef;
import com.hzgj.bcl.soa.registry.Consumer;
import com.hzgj.bcl.soa.util.Constants;
import com.hzgj.bcl.soa.util.Pair;
import com.hzgj.bcl.util.IocContext;
import com.hzgj.bcl.util.lang.StrKit;
import lombok.Builder;
import lombok.Getter;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static com.hzgj.bcl.soa.command.CommandsInitializer.CLIENT_COMMAND;
import static com.hzgj.bcl.soa.command.CommandsInitializer.REG_COMMAND;
import static com.hzgj.bcl.soa.command.RegistryCommand.*;
import static com.hzgj.bcl.util.lang.ThreadPoolKit.getScheduledExecutorService;

/**
 * Created by warning5 on 2017/3/28.
 */
public class ClientCommand extends BaseCommand {

    EtcdClient etcdClient;
    Map<String, EtcdClient.ClientInfo> clientInfos = Maps.newHashMap();
    public static final String COMMAND_CLIENT_COUNT = "count";

    @Override
    public void start() {
        etcdClient = IocContext.applicationContext.getBean(EtcdClient.class);
        ScheduledExecutorService scheduledExecutorService = getScheduledExecutorService("cache-clientInfo");
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            try {
                buildClientInfo();
            } catch (Exception e) {
                logger.error("{}", e);
            }
        }, 0, 5, TimeUnit.SECONDS);
    }

    public List<Consumer> getClientDetail(String service) {
        return etcdClient.getConsumes(service);
    }

    private void buildClientInfo() {
        Map<String, EtcdClient.ClientInfo> _clientInfos = etcdClient.getConsumes();
        clientInfos = _clientInfos;
    }

    @Override
    public String invoke(CommandOptions commandOptions) {

        String[] secondCommand = commandOptions.params.get(SoaCommand.secondCommand);
        if (StrKit.notBlank(secondCommand)) {
            switch (secondCommand[0]) {
                case COMMAND_DELETE:
                    String[] service_param = commandOptions.params.get(SERVICE);
                    String service = null;
                    if (service_param != null && StrKit.notBlank(service_param[0])) {
                        service = service_param[0].trim();
                    }
                    return delete(service);
                case COMMAND_NODES:
                    return nodes();
                case COMMAND_CLIENT_COUNT:
                    String services = commandOptions.params.get("services")[0];
                    return count(services);
                default:
                    return list();
            }
        } else {
            return list();
        }
    }

    private String count(String index_service) {
        String[] split = index_service.split(",");
        List<Pair<String, Integer>> result = Lists.newArrayList();
        for (String item : split) {
            String[] indexAndService = item.split(":");
            EtcdClient.ClientInfo clientInfo = clientInfos.get(indexAndService[1]);
            Pair<String, Integer> pair = new Pair<>(indexAndService[0], 0);
            if (clientInfo != null) {
                pair.setRight(clientInfo.count);
            }
            result.add(pair);
        }
        return JSON.toJSONString(result);
    }

    public int getCount(String service) {
        EtcdClient.ClientInfo clientInfo = clientInfos.get(service);
        if (clientInfo != null) {
            return clientInfo.count;
        }
        return 0;
    }

    private String delete(String service) {
        Map<String, Object> result = Maps.newHashMap();
        if (StrKit.isBlank(service)) {
            result.put("success", false);
            result.put("msg", "service cant't be null");
        } else {
            try {
                etcdClient.remClient(service);
                clientInfos.remove(service);
                result.put("success", true);
            } catch (Exception e) {
                logger.error("{}", e);
                result.put("success", false);
                result.put("msg", e.getMessage());
            }
        }
        return JSON.toJSONString(result);
    }

    private String list() {
        Map map = Maps.newHashMap();
        map.put("data", clientInfos.values());
        return JSON.toJSONString(map);
    }

    @Override
    public String getName() {
        return CLIENT_COMMAND;
    }


    private String nodes() {
        Map map = Maps.newHashMap();
        map.put("data", getClientNodes().values());
        return JSON.toJSONString(map);
    }

    private Map<String, ClientNodeInfo> getClientNodes() {
        RegistryCommand registryCommand = CommandsInitializer.getCommand(REG_COMMAND);
        Map<String, ServiceDef.ServiceInfo> serviceInfos = registryCommand.getServiceInfos();
        Map<String, ClientNodeInfo> clientNodeInfos = Maps.newHashMap();
        serviceInfos.keySet().forEach(name -> {
            List<Consumer> consumers = getClientDetail(name);
            consumers.forEach(consumer -> {
                clientNodeInfos.compute(consumer.getHost(), (address, nodeInfo) -> {
                    if (nodeInfo == null) {
                        nodeInfo = ClientNodeInfo.builder().count(1).address(consumer.getHost()).jmxPort(consumer.getJmxPort()).build();
                    } else {
                        nodeInfo.add();
                    }
                    return nodeInfo;
                });
            });
        });
        return clientNodeInfos;
    }

    public List<Consumer> getClientsByAddress(String address) {
        RegistryCommand registryCommand = CommandsInitializer.getCommand(REG_COMMAND);
        Map<String, ServiceDef.ServiceInfo> serviceInfos = registryCommand.getServiceInfos();
        List<Consumer> consumers = Lists.newArrayList();
        serviceInfos.keySet().forEach(name -> {
            consumers.addAll(etcdClient.getConsumes(name, address));
        });
        return consumers;
    }

    public List<CicadaRouter.ClientPoolInfo> getClientPoolInfo(String[] address) {
        JmxCollector jmxCollector = Collectors.getCollector(address[0], Integer.parseInt(address[1]));
        if (jmxCollector != null) {
            try {
                return (List<CicadaRouter.ClientPoolInfo>) jmxCollector.invokeWithoutParam(Constants.getCicadaRouterObjectName(), "getPoolInfo");
            } catch (Exception e) {
                logger.error("{}", e);
                Collectors.removeCollector(address[0], Integer.parseInt(address[1]));
            }
        }
        return Collections.emptyList();
    }

    @Override
    public int getSequence() {
        return 10;
    }

    @Getter
    @Builder
    public static class ClientNodeInfo {
        int count;
        String address;
        int jmxPort;

        public void add() {
            count++;
        }
    }
}
